using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using XTengine.ComponentModel;

namespace XTengine
{
    public class Level
    {
        private string name;

        public List<XTengineObject> Entities { get; private set; }

        private StringBuilder builder;

        public Level(string name)
        {
            this.name = name;

            Entities = new List<XTengineObject>();
        }

        public void AddEntity(XTengineObject entity)
        {
            this.Entities.Add(entity);
        }

        public void RemoveEntity(XTengineObject entity)
        {
            this.Entities.Remove(entity);
        }

        public void Clear()
        {
            this.Entities.Clear();
        }

        private void InsertLine()
        {
            builder.Append("\r\n");
        }

        private void InsertSpace()
        {
            builder.Append(" ");
        }

        public bool Load(ComponentManager manager)
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var stream = new IsolatedStorageFileStream(name + ".lvl", FileMode.OpenOrCreate, FileAccess.Read, store))
                {
                    byte[] levelBytes = new byte[stream.Length];
                    stream.Read(levelBytes, 0, (int)stream.Length);

                    System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding();
                    string fileContents = encoding.GetString(levelBytes, 0, levelBytes.Length);

                    if (string.IsNullOrEmpty(fileContents))
                        return false;

                    string[] lines = Regex.Split(fileContents, "\r\n");

                    ProcessLinesForLevel(manager, lines);

                    return true;
                }
            }
        }

        private void ProcessLinesForLevel(ComponentManager manager, string[] lines)
        {
            XTengineObject currentEntity = null;

            foreach (string line in lines)
            {
                if (line == null || line.Length == 0)
                    continue;

                string[] words = line.Split(' ');

                string lineType = words[0];

                if (lineType == null || lineType.Length == 0)
                    continue;

                if (lineType == "Entity")
                {
                    currentEntity = manager.CreateEntity();
                    this.AddEntity(currentEntity);

                    continue;
                }

                words = words.Skip(1).ToArray();

                string assqualName = lineType;
                string typeName = lineType.Split('.').Last();

                Assembly typeOwner = ComponentManager.ComponentAssemblyMap[typeName];

                Type componentType = typeOwner.GetType(assqualName);

                if (componentType == null)
                    continue;

                XTengineComponentBase component = currentEntity.AddComponent(componentType) as XTengineComponentBase;

                if (component == null)
                    continue;

                for (int i = 0; i < words.Length; i += 2)
                    component.SetProperty(componentType, words[i], words[i + 1]);
            }
        }

        public void Save()
        {
            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var stream = new IsolatedStorageFileStream(name + ".lvl", FileMode.Create, FileAccess.Write, store))
                {
                    builder = new StringBuilder();

                    InsertLine();

                    foreach (XTengineObject entity in Entities)
                    {
                        builder.Append("Entity");
                        InsertLine();

                        BuildLinesForComponent(entity);

                        InsertLine();
                    }

                    System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding();
                    byte[] levelBytes = encoding.GetBytes(builder.ToString());

                    stream.Write(levelBytes, 0, levelBytes.Length);

                    builder = null;
                }
            }

        }

        private void BuildLinesForComponent(XTengineObject entity)
        {
            foreach (Type componentType in entity.Components.Keys)
            {
                builder.Append(componentType.Namespace + "." + componentType.Name);

                Dictionary<string, PropertyInfo> componentProperties;
                bool foundMap = ComponentManager.ComponentPropertyMap.TryGetValue(componentType, out componentProperties);

                IXTengineComponent component = entity.Components[componentType];

                foreach (string property in componentProperties.Keys)
                {
                    PropertyInfo propertyInfo = componentProperties[property];

                    string propertyValue = string.Empty;
                    if (propertyInfo.PropertyType.IsEnum)
                    {
                        propertyValue = propertyInfo.GetValue(component, null).ToString();
                    }
                    else
                    {
                        Type converterType = TypeConverters.GetConverterForType(propertyInfo.PropertyType);

                        if (converterType == null)
                            continue;

                        MethodInfo info = converterType.GetMethod("ConvertTo", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { propertyInfo.PropertyType }, null);

                        if (info == null)
                            continue;

                        object[] arguments = { propertyInfo.GetValue(component, null) };

                        propertyValue = info.Invoke(Activator.CreateInstance(converterType), BindingFlags.Public | BindingFlags.Instance, null, arguments, System.Globalization.CultureInfo.CurrentCulture) as string;
                    }

                    InsertSpace();
                    builder.Append(property);
                    InsertSpace();

                    builder.Append(propertyValue);
                }

                InsertLine();
            }
        }
    }
}
